home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Basic Toolbox
/
Visual Basic Toolbox (P.I.E.)(1996).ISO
/
code_lib
/
objlibr
/
objlib12
/
vboop2.txt
< prev
Wrap
Text File
|
1995-04-26
|
16KB
|
398 lines
#: 26702 S2/Beginner's Corner
21-Feb-95 14:50:28 Thread=J20
Sb: #26247-OO vb
Fm: Kathleen Joeris 75330,156
To: Pete Washburn 73750,3141
Pete,
Sorry I did not reply sooner; I was busy over the weekend.
(For anyone who wants to look at Pete's article: it is in the Beginner's
Corner (sect 2) as VBOOP.ZIP.)
Pete: I do not understand one aspect of what you are doing. This has to do
with creating a new object of an inherited class.
First question is what hOBJ is passed when the NEW_OBJECT is called?
Second question, when does the pipeInstance and WaterpartInstance arrays get
redimensioned?
Third question, what is the UDT for the pipe class?
Fourth question, is the following description of what happens when a new pipe
is created correct?
First, the pipeclass is called with a NEW_OBJECT message.
Since this message is not handled by the pipeclass, it is passed to the
waterpartclass with the NEW_OBJECT message.
I expected this to create a new element in the WaterpartInstances array
but I did not see a REDIM PRESERVE statement. Did I miss something here?
You then use the self function to call pipeclass, changing the message to
INIT_OBJECT
INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
waterpartclass
You then use the self function to call pipeclass, changing the message to
SET_DIAMETER
The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
This is the deepest point of the stack, and the stack is
WaterPart (message = SET_DIAMETER)
Pipe (message = SET_DIAMETER)
Self (message = SET_DIAMETER)
WaterPart (message = INIT_OBJECT)
Pipe (message = INIT_OBJECT)
Self (message = INIT_OBJECT)
WaterPart (message = NEW_OBJECT)
Pipe (message = NEW_OBJECT)
The return value of Pipe(...SET_DIAMETER...) is an empty variant. This
value is passed back, eventually to the calling routine.
I took a good bit of time with this. I was unable to convince myself that it
would work as stated. Perhaps you can explain where I missed the boat. Have
you actually used this code, or was it over simplified when you wrote this?
This seems to fully implement polymorphism, which in my mind is more important
than inheritance. I really like this aspect of your design.
You and Deborah hide the data within the function/module. I can see the
benefits of doing this, but do not do it that way. Ultimately the speed of
the seek is critical to this being practical. As I understand this design, you
scan arrays eight times to perform a NEW_OBJECT. This shows why I do not do
it this way.
I believe that your inheritance would work. I think I either misunderstood
some things, or it needs some fixes to this code, but the underlying theory
seems entirely sound. I understand why you go through the inheritance stack
three times, but it is still a bit unwieldly. This would particularly be a
problem for a deeper inheritance stack.
I am very curious as to how you handle the inheritance of DATA. You do not
appear to do it in either of the manners I would have tried. One would have
been
Type PipeClass
Parent as WaterPartClass
Length as Double
EndType
and the other would have been to disallow any access to waterpart data of the
pipe object except through messages sent to the waterpart class.
THere are, as you mention, a number of things that could be done to make this
more efficient. However, I think the basic theory is there in what you are
doing.
Have a good day, Kathleen
=======================================================================
#: 375932 S0/Outbox File
22-Feb-95 17:32:00
Sb: OO vb
Fm: VBPJFO REP 26702
To: Kathleen Joeris 75330,156
Hi Kathleen!
Boy, you sure dug in! Good questions all. Let me see if I can clarify them
somewhat.
>First question is what hOBJ is passed when the NEW_OBJECT is called?
I'm passing a Null when I create a new object as in:
manifold1 = Pipe(Null, NEW_OBJECT, "2.5 100")
The first thing NEW_OBJECT does is checks the hObj passed to it. If it is
Null, then a new object is created (actually the object's handle). The hObj
is created by ObjectDirectory and returned from the function. You
then have a global handle to the object, in this example, manifold1
>Second question, when does the pipeInstance and WaterpartInstance
>arrays get redimensioned?
I'm showing my age and experience with QB 2.0, et. al. The days before
Redim Preserve! Could very well use ReDim Preserve. By the way, how fast
is it? Of course, one of my other answers will deal with performance issues
and I'm sure it won't be significant as new objects aren't created all that
often.
In this example, the arrays are dimensioned during the INIT_CLASS method, as
in:
a = Pipe(Null, INIT_CLASS, 5)
In this case, I'm not planning on more than 5 instances of this class, so
the instance arrays are dimmed to 5 elements. No reason that you couldn't
get rid of the INIT_CLASS method however and just Redim Preserve as
necessary. Much more dynamic that way too!!
>Third question, what is the UDT for the pipe class?
I'm sorry, I can't recall what "UDT" is.
>Fourth question, is the following description of what happens when a new
>pipe is created correct?
I don't have the actual file handy, but here's what I've got for NEW_OBJECT
for the pipe class in my code here:
Case NEW_OBJECT
' get a new object handle if it hasn't already been created
If IsNull(hObj) Then
hObj = ObjectDirectory(Null, NEW_OBJECT, PIPE_CLASS)
End If
' create a new object instance in this class
pipeCount = pipeCount + 1
hInst = pipeCount
objectInstanceHandles(hObj) = hInst
' save the object handle in the instance
pipeInstances(hInst).hObj = hObj
' create the ancestor instances
a = WaterPart(hObj, NEW_OBJECT, value)
' initialize the object
a = Pipe(hObj, INIT_OBJECT, parse(value))
' return the object's handle
Pipe = hObj
Here's what's going on. If an object handle hasn't been created by a
descendant class, then the first thing done is to create one. Next, the
instance data is created. To speed access to the instance data later, the
instance's data array index is stored in another array indexed by the
object's handle. Then, the Pipe class passes the message on to the ancestor
class, WaterPart in this case, so the WaterPart instances can be created for
this object. Finally, the object is initialized with INIT_OBJECT.
> First, the pipeclass is called with a NEW_OBJECT message.
Yes
> Since this message is not handled by the pipeclass, it is passed to the
> waterpartclass with the NEW_OBJECT message.
Yes it is handled by pipeClass and then passed on. Sorry if the file you
downloaded didn't have that.
> I expected this to create a new element in the WaterPartInstances array
> but I did not see a REDIM PRESERVE statement. Did I miss something here?
As discussed above, the PipeInstances array was dimmed earlier during
INIT_CLASS in this example. Could ReDim Preserve.
> You then use the self function to call pipeclass, changing the message to
> INIT_OBJECT
Yes. Now that all of the instance variables in the ancestor classes have
been created, its safe to do all of the intializing that needs to be done.
Here's the code for Pipe INIT_OBJECT:
Case INIT_OBJECT
Pipe = Self(hObj, SET_LENGTH, value)
For this class, the only thing that has to be done is set the lenght of the
pipe. Self is used just in case a descendant class has another way of
processing SET_LENGTH. Note that when the object's handle hObj was created,
one of the parameters to that call was the class of the object, the
PIPE_CLASS in this case. When the object is created, the object's class is
saved so that Self will know where to call to process any methods. So if
this object was a descendant of Pipe and had another SET_LENGTH method, it
would be called instead of the Pipe class message. If none of the
descendant classes process the method, it will eventually get passed back
here to the Pipe class.
> INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
> waterpartclass
Yes it is. See above. (Sorry if this is different than in the file) Note
that each class's NEW_OBJECT calls INIT_OBJECT. This allows the instance
variables for that class to be initialized. So the WaterPart INIT_OBJECT is
processed before control is returned back to Pipe NEW_OBJECT
(Continued)
S2
#: 375938 S0/Outbox File
22-Feb-95 17:38:00
Sb: OO vb
Fm: VBPJFO REP 26702
To: Kathleen Joeris 75330,156
(continued)
> You then use the self function to call pipeclass, changing the message to
> SET_DIAMETER
> The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
> This is the deepest point of the stack, and the stack is
> WaterPart (message = SET_DIAMETER)
> Pipe (message = SET_DIAMETER)
> Self (message = SET_DIAMETER)
> WaterPart (message = INIT_OBJECT)
> Pipe (message = INIT_OBJECT)
> Self (message = INIT_OBJECT)
> WaterPart (message = NEW_OBJECT)
> Pipe (message = NEW_OBJECT)
I suspect my code has updated that somewhat. Now, SET_DIAMETER is handled
during WaterPart's INIT_OBJECT which is called from WaterPart's NEW_OBJECT.
So the stack would be:
WaterPart (message = SET_DIAMETER)
Pipe (message = SET_DIAMETER)
Self (message = SET_DIAMETER)
WaterPart (message = INIT_OBJECT)
WaterPart (message = NEW_OBJECT)
Pipe (message = NEW_OBJECT)
> The return value of Pipe(...SET_DIAMETER...) is an empty variant. This
> value is passed back, eventually to the calling routine.
In my working code, hObj get's passed back so you have a handle to the newly
created object. The Pipe function (class) does pass back a variant type so
that I have the flexiblity to return basically whatever is needed back.
Before the variant type existed in VB, I returned everything as a string
which could be then converted into whatever data type was needed. So even
though I didn't catch on to the ReDim Preserve, I did see Variant as an
enhancement to this OO thinking!
> I took a good bit of time with this. I was unable to convince myself that
> it would work as stated. Perhaps you can explain where I missed the boat.
> Have you actually used this code, or was it over simplified when you wrote
> this?
The file consist of some messages I made several months ago (almost a year)
During that time, the design has been enhanced and improved and hopefully
with what I've added here, will make more sense. There wasn't much interest
in the thread back then, so I hadn't spent a lot of time keeping it current.
The code I've shown today is actual code that seems to be doing quite well,
although I'm always trying to improve things (who isn't!)
> This seems to fully implement polymorphism, which in my mind is more
> important than inheritance. I really like this aspect of your design.
> You and Deborah hide the data within the function/module. I can see the
> benefits of doing this, but do not do it that way. Ultimately the speed
> of the seek is critical to this being practical. As I understand this
> design, you scan arrays eight times to perform a NEW_OBJECT. This shows
> why I do not do it this way.
> I believe that your inheritance would work. I think I either
> misunderstood some things, or it needs some fixes to this code, but the
> underlying theory seems entirely sound. I understand why you go through
> the inheritance stack three times, but it is still a bit unwieldly.
> This would particularly be a problem for a deeper inheritance stack.
I think this nearly completely encompasses the three major features of OO
design. Encapsulation, inheritance, and polymorphism. VB certainly isn't
optimized for this type of design, but you can meet most of the principles
this way and receive 95% of the benefits of OO design.
I think the performance issues of any PURE OO design will always sacrifice
speed for those benefits. It certainly was true with Actor (a Smalltalk
like language). There sure is alot of message passing going on! I just
don't see many ways to streamline the process without violating or losing
some of the OO benefits. If you don't hardcode in the object's class, you
need to make all those calls to Self to allow any descendant class it's
chance to process the method. And if you don't pass any unhandled methods
up to an ancestor, you're not gaining the inheritance benefits. If you
start opening up the instance data globally, then you've lost the benefits
of encapsulation.
As always, life's a tradeoff. In my applications, 99.9% of the time the
computer is waiting for the user to click on something, so the small
performance penalty is insignificant compared with the easy of designing and
maintaining the program. If this was a real time control program, then that
equation would obviously change. Although I've not played with it, I think
C++ is a reasonable tradeoff between OO principles and performance issues.
You can take those shortcuts when necessary for performance with the full
understanding for what you're giving up. With these techniques, I can do
pretty much the same thing with VB. That wasn't an option with Actor; you
always had do to things in a pure OO way.
> I am very curious as to how you handle the inheritance of DATA. You do
> not appear to do it in either of the manners I would have tried. One
> would have been
> Type PipeClass
> Parent as WaterPartClass
> Length as Double
> EndType
> and the other would have been to disallow any access to waterpart data of
> the pipe object except through messages sent to the waterpart class.
This is one of the two major limitations of doing this with VB; there isn't
an easy way to inherit the data from an ancestor class. You could do it by
including an instance variable of the ancestor's class in the class's
instance variables as in:
Type pipeObject
hObj As Integer
waterPart as WaterPartObject
length As Integer
End Type
But then you have the problem of keeping the contents of waterPart the same
as the instance variables in WaterPart. Plus if you get more than a couple
of levels deep, it quickly becomes unmanageable. So I had to accept almost
pure encapsulation in each class, even from it's ancestors. A pain in
theory, but so far, hasn't been too much of a problem.
The other major limitation was the need to track the class of each object
and indexing it's instance data in the various classes. This is something
that is automatic in the other OO languages I've seen.
> There are, as you mention, a number of things that could be done to make
> this more efficient. However, I think the basic theory is there in what
> you are doing.
Thanks. It's always nice to be appreciated :) Actually, I'm hopeing to
gain some suggestions about improving it somewhat. Your suggestion of ReDim
Preserve fits into that category, thanks.
Pete Washburn,
W.W. Programming
#: 381305 S0/Outbox File
26-Feb-95 11:05:00
Sb: OO vb
Fm: VBPJFO REP 27047
To: Kathleen Joeris [SL-1] 75330,156
Hi Kathleen,
UDT - User Defined Type. I knew it looked familiar; just couldn't quite get
a handle on it! Here's the pipe object:
' pipe objects
Type pipeObject
hObj As Integer
length As Integer ' in feet
volume As Integer ' in cubic feet
change As Integer ' true/false
End Type
Pete Washburn,
W.W. Programming